package org.jeecg.common.util;

import com.google.zxing.*;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.springframework.beans.factory.annotation.Value;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;

public class QRCodeUtil {

	public static void main(String[] args) throws Exception {
		String webUrl = "3131"; // 目标网址
		URL imageUrl = ClassLoader.getSystemResource("./static/logo.png");
		int qrCodeSize = 200; // 二维码尺寸设置为300x300像素
		int logoSize = 50; // 定义Logo的宽度，留白区域大小

		// 调用函数生成带留白无logo的二维码图像
		BufferedImage qrCodeImage = generateQRCodeImage(webUrl, qrCodeSize, qrCodeSize, logoSize);

		// 调用函数在二维码中心加入logo
		BufferedImage qrCodeWithLogo = addLogoToQRCode(qrCodeImage, imageUrl.getPath(), 100, 100, 200, 200);

		// 调用函数将带有logo的二维码图像转换为Base64字符串
		String base64Encoded = convertImageToBase64(qrCodeWithLogo);

		System.out.println(base64Encoded); // 输出二维码的Base64编码字符串
	}


	/**
	 * 生成带有中部空白区（为Logo预留空间）的二维码图像。
	 * @param data 要编码到二维码中的数据字符串
	 * @param width 生成的二维码图像宽度
	 * @param height 生成的二维码图像高度
	 * @param logoSize 预留给logo的空白区域的宽度与高度（假设空白区域为正方形）
	 * @return 中央带有空白区域的二维码图像
	 * @throws Exception 如果生成二维码时遇到任何错误，则抛出异常
	 */
	public static BufferedImage generateQRCodeImage(
			String data, int width, int height, int logoSize) throws Exception {

		// 配置二维码生成参数
		HashMap<EncodeHintType, Object> hints = new HashMap<>();
		hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 指定二维码的编码设置为UTF-8
		hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // 设置二维码的纠错级别为高(H)
		hints.put(EncodeHintType.MARGIN, 0); // 设置二维码边缘 空背空白（边框大小）为0

		// 根据数据与配置信息生成二维码的比特矩阵
		BitMatrix bitMatrix = new MultiFormatWriter().encode(data, BarcodeFormat.QR_CODE, width, height, hints);

		// 将二维码比特矩阵转换为BufferedImage图像，用于最终输出
		BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix);

		// 计算中部logo空白区域的坐标位置
		// 这确保了空间位于二维码的正中心
		int deltaHeight = image.getHeight() - logoSize;
		int deltaWidth = image.getWidth() - logoSize;
		int coordX = deltaWidth / 2;
		int coordY = deltaHeight / 2;

//		for (int i = 0; i < width; i++) {
//			for (int j = 0; j < height; j++) {
//				image.setRGB(i, j, bitMatrix.get(i, j) ? Color.BLACK.getRGB() : Color.white.getRGB());
//			}
//		}

		// 采用图形对象在图像上绘图
		Graphics2D g2 = image.createGraphics();

		 //设置compositing 规则定为SRC，通过AlphaComposite指定透明度效果
		g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));

		// 设置Logo区域的填充颜色为白色
		g2.setPaint(Color.WHITE);
		// 绘制一个填充过的矩形以形成预留给logo的空白区域
		g2.fillRect(coordX, coordY, logoSize, logoSize);

		g2.dispose(); // 结束图形编辑并释放系统资源

		return image; // 返回生成的二维码BufferedImage对象
	}

	/**
	 * 将一个给定的logo图片添加到二维码中心
	 *
	 * @param qrCodeImage 二维码的BufferedImage对象
	 * @param logoPath logo图片的路径
	 * @return 合成后的带有logo的二维码图片
	 * @throws IOException 如果读取logo文件失败
	 */
	public static BufferedImage addLogoToQRCode(BufferedImage qrCodeImage, String logoPath, int width, int height, int xPosition, int yPosition) throws IOException {
		// 从文件中读取logo图片
		BufferedImage logoImage = ImageIO.read(new File(logoPath));

		// 创建一个新的缓冲区图像，其中包含alpha值(透明度)用来画带边界的logo
		BufferedImage logoWithBorder = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		// 使用Graphics2D绘制带边界的logo
		Graphics2D gBorder = logoWithBorder.createGraphics();

		// 设置合成规则，表示后续绘图操作将直接替换背景像素
		gBorder.setComposite(AlphaComposite.Src);

		// 设置抗锯齿属性来平滑边缘
		gBorder.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

		// 绘制一个填满整个logoWithBorder尺寸的白色矩形，形成logo的边界
		gBorder.setColor(Color.WHITE);
		gBorder.fillRect(0, 0, width, height);

		// 将logo图片缩放并绘制到logoWithBorder上
		gBorder.drawImage(logoImage.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);

		// 不再进行绘制操作，释放此图形的上下文以及它使用的任何系统资源
		gBorder.dispose();


		int alpha = (qrCodeImage.getRGB(1, 1) >> 24) & 0xff;  // 提取某像素透明度（0-255）
		System.out.println("alpha = " + alpha);

		// 创建一个新的缓冲区图像以容纳原始的二维码和放置其中的logo
		BufferedImage combined = new BufferedImage(qrCodeImage.getWidth(), qrCodeImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = (Graphics2D) combined.getGraphics();

		g.setComposite(AlphaComposite.Clear); // 清除画布
		g.fillRect(0, 0, qrCodeImage.getWidth(), qrCodeImage.getHeight());      // 填充透明
		g.setComposite(AlphaComposite.SrcOver); // 恢复默认混合模式

		int alpha1 = (qrCodeImage.getRGB(1, 1) >> 24) & 0xff;  // 提取某像素透明度（0-255）
		System.out.println("alpha1 = " + alpha1);

		// 将二维码绘制在新图片的最底层
		g.drawImage(qrCodeImage, 0, 0, null);

		// 设置合成效果来绘制logo，覆盖在之前绘制的内容之上
		g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f));

		// 在合适的位置绘制带有白边背景框的logo（确保居中）
		g.drawImage(logoWithBorder, Math.round(xPosition / 2), Math.round(yPosition / 2), null);

		// 完成绘制，释放图形上下文使用的资源
		g.dispose();

		// 返回合成后带有logo的二维码图像
		return combined;
	}

	public static BufferedImage addTextToImg(BufferedImage originalImage, String fontPath, String text, int XPosition, int YPosition) {
		try {
			// 2. 创建可编辑的图像副本
			BufferedImage newImage = new BufferedImage(
					originalImage.getWidth(),
					originalImage.getHeight(),
					BufferedImage.TYPE_INT_RGB
			);
			// 3. 获取图形上下文
			Graphics2D graphics = newImage.createGraphics();
			graphics.drawImage(originalImage, 0, 0, null);

			// 4. 设置字体和颜色
			Font font = Font.createFont(Font.TRUETYPE_FONT, new File(fontPath + "/PINGFANGSC-REGULAR.OTF")).deriveFont(45f);
			graphics.setFont(font);
			graphics.setColor(Color.black);

			// 5. 添加文字(参数：文字内容，x坐标，y坐标)
			graphics.drawString(text, XPosition, YPosition);

			// 6. 释放资源
			graphics.dispose();

			// 7. 保存新图片
			return newImage;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 将图像转换成Base64编码的字符串
	 * @param image
	 * @return
	 * @throws Exception
	 */
	public static String convertImageToBase64(BufferedImage image) throws Exception {
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // 创建ByteArrayOutputStream对象
		ImageIO.write(image, "png", outputStream); // 把BufferedImage写入流中
		byte[] imageBytes = outputStream.toByteArray(); // 转换成对应的byte数组
		return Base64.getEncoder().encodeToString(imageBytes); // 进行Base64编码，并返回字符串
	}
}
